﻿using System;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Telerik.Web.UI.Widgets;
using System.Collections.Generic;
using System.Collections;

namespace FileServer.DataConnectors
{
	//FileBrowserContentProvider
	public class DBContentProvider : FileBrowserContentProvider
	{
		private DataServer _dataServer;
		private DataServer DataServer
		{
			get
			{
				if (Object.Equals(_dataServer, null))
				{
					_dataServer = new DataServer();
				}
				return _dataServer;
			}
		}

		private string _itemHandlerPath;
		private string ItemHandlerPath
		{
			get
			{
				return _itemHandlerPath;
			}
		}

		public DBContentProvider(HttpContext context, string[] searchPatterns, string[] viewPaths, string[] uploadPaths, string[] deletePaths, string selectedUrl, string selectedItemTag)
			:
			base(context, searchPatterns, viewPaths, uploadPaths, deletePaths, selectedUrl, selectedItemTag)
		{
			_itemHandlerPath = ConfigurationManager.AppSettings["Telerik.WebControls.EditorExamples.ItemHandler"];
			if (_itemHandlerPath.StartsWith("~/"))
			{
				_itemHandlerPath = HttpContext.Current.Request.ApplicationPath + _itemHandlerPath.Substring(1);
			}
			if (SelectedItemTag != null && SelectedItemTag != string.Empty)
			{
				SelectedItemTag = ExtractPath(RemoveProtocolNameAndServerName(SelectedItemTag));
			}
		}


		private string GetItemUrl(string virtualItemPath)
		{
			string escapedPath = Context.Server.UrlEncode(virtualItemPath);
			return string.Format("{0}?path={1}", ItemHandlerPath, escapedPath);
		}

		private string ExtractPath(string itemUrl)
		{
			if (itemUrl == null)
			{
				return string.Empty;
			}
			if (itemUrl.StartsWith(_itemHandlerPath))
			{
				return itemUrl.Substring(GetItemUrl(string.Empty).Length);
			}
			return itemUrl;
		}

		private string GetName(string path)
		{
			if (String.IsNullOrEmpty(path) || path == "/")
			{
				return string.Empty;
			}
			path = VirtualPathUtility.RemoveTrailingSlash(path);
			return path.Substring(path.LastIndexOf('/') + 1);
		}


		private string GetDirectoryPath(string path)
		{
			return path.Substring(0, path.LastIndexOf('/') + 1);
		}

		private bool IsChildOf(string parentPath, string childPath)
		{
			return childPath.StartsWith(parentPath);
		}

		private string CombinePath(string path1, string path2)
		{
			if (path1.EndsWith("/"))
			{
				return string.Format("{0}{1}", path1, path2);
			}
			if (path1.EndsWith("\\"))
			{
				path1 = path1.Substring(0, path1.Length - 1);
			}
			return string.Format("{0}/{1}", path1, path2);
		}

		private DirectoryItem[] GetChildDirectories(string path)
		{
			List<DirectoryItem> directories = new List<DirectoryItem>();
			try
			{
				DataRow[] childRows = DataServer.GetChildDirectoryRows(path);
				int i = 0;
				while (i < childRows.Length)
				{
					DataRow childRow = childRows[i];
					string name = childRow["Name"].ToString();
					string itemFullPath = VirtualPathUtility.AppendTrailingSlash(CombinePath(path, name));

					DirectoryItem newDirItem = new DirectoryItem(name,
																 string.Empty,
																 itemFullPath,
																 string.Empty,
																 GetPermissions(itemFullPath),
																 null, // The files are added in ResolveDirectory() 
																 null // Directories are added in ResolveRootDirectoryAsTree()
																 );

					directories.Add(newDirItem);
					i = i + 1;
				}
				return directories.ToArray();
			}
			catch (Exception)
			{
				return new DirectoryItem[] { };
			}
		}

		private FileItem[] GetChildFiles(string _path)
		{
			try
			{
				DataRow[] childRows = DataServer.GetChildFileRows(_path);
				List<FileItem> files = new List<FileItem>();

				for (int i = 0; i < childRows.Length; i++)
				{
					DataRow childRow = childRows[i];
					string name = childRow["Name"].ToString();
					if (IsExtensionAllowed(System.IO.Path.GetExtension(name)))
					{
						string itemFullPath = CombinePath(_path, name);

						FileItem newFileItem = new FileItem(name,
															Path.GetExtension(name),
															(int)childRow["Size"],
															itemFullPath,
															GetItemUrl(itemFullPath),
															string.Empty,
															GetPermissions(itemFullPath)
															);

						files.Add(newFileItem);
					}
				}
				return files.ToArray();
			}
			catch (Exception)
			{
				return new FileItem[] { };
			}
		}

		private bool IsExtensionAllowed(string extension)
		{
			return Array.IndexOf(SearchPatterns, "*.*") >= 0 || Array.IndexOf(SearchPatterns, "*" + extension.ToLower()) >= 0;
		}

		/// <summary>
		/// Checks Upload permissions
		/// </summary>
		/// <param name="path">Path to an item</param>
		/// <returns></returns>
		private bool HasUploadPermission(string path)
		{
			foreach (string uploadPath in this.UploadPaths)
			{
				if (path.StartsWith(uploadPath, StringComparison.CurrentCultureIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Checks Delete permissions
		/// </summary>
		/// <param name="path">Path to an item</param>
		/// <returns></returns>
		private bool HasDeletePermission(string path)
		{
			foreach (string deletePath in this.DeletePaths)
			{
				if (path.StartsWith(deletePath, StringComparison.CurrentCultureIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Returns the permissions for the provided path
		/// </summary>
		/// <param name="pathToItem">Path to an item</param>
		/// <returns></returns>
		private PathPermissions GetPermissions(string pathToItem)
		{
			PathPermissions permission = PathPermissions.Read;
			permission = HasUploadPermission(pathToItem) ? permission | PathPermissions.Upload : permission;
			permission = HasDeletePermission(pathToItem) ? permission | PathPermissions.Delete : permission;

			return permission;
		}


		/// <summary>
		/// Loads a root directory with given path, where all subdirectories 
		/// contained in the SelectedUrl property are loaded
		/// </summary>
		/// <remarks>
		/// The ImagesPaths, DocumentsPaths, etc properties of RadEditor
		/// allow multiple root items to be specified, separated by comma, e.g.
		/// Photos,Paintings,Diagrams. The FileBrowser class calls the 
		/// ResolveRootDirectoryAsTree method for each of them.
		/// </remarks>
		/// <param name="path">the root directory path, passed by the FileBrowser</param>
		/// <returns>The root DirectoryItem or null if such does not exist</returns>
		public override DirectoryItem ResolveRootDirectoryAsTree(string path)
		{
			DirectoryItem returnValue = new DirectoryItem(GetName(path),
															GetDirectoryPath(path),
															path,
															string.Empty,
															GetPermissions(path),
															null, // The files  are added in ResolveDirectory()
															GetChildDirectories(path));
			return returnValue;
		}

		public override DirectoryItem ResolveDirectory(string path)
		{
			DirectoryItem[] directories = GetChildDirectories(path);

			DirectoryItem returnValue = new DirectoryItem(GetName(path),
															  VirtualPathUtility.AppendTrailingSlash(GetDirectoryPath(path)),
															  path,
															  string.Empty,
															  GetPermissions(path),
															  GetChildFiles(path),
															  null // Directories are added in ResolveRootDirectoryAsTree()
															  );

			return returnValue;
		}

		public override string GetFileName(string url)
		{
			return GetName(url);
		}

		public override string GetPath(string url)
		{
			return GetDirectoryPath(ExtractPath(RemoveProtocolNameAndServerName(url)));
		}

		public override Stream GetFile(string url)
		{
			byte[] content = DataServer.GetContent(ExtractPath(RemoveProtocolNameAndServerName(url)));
			if (!Object.Equals(content, null))
			{
				return new MemoryStream(content);
			}
			return null;
		}

		public override string StoreBitmap(Bitmap bitmap, string url, ImageFormat format)
		{
			string newItemPath = ExtractPath(RemoveProtocolNameAndServerName(url));
			string name = GetName(newItemPath);
			string _path = GetPath(newItemPath);
			string tempFilePath = System.IO.Path.GetTempFileName();
			bitmap.Save(tempFilePath);
			byte[] content;
			using (FileStream inputStream = File.OpenRead(tempFilePath))
			{
				long size = inputStream.Length;
				content = new byte[size];
				inputStream.Read(content, 0, Convert.ToInt32(size));
			}

			if (File.Exists(tempFilePath))
			{
				File.Delete(tempFilePath);
			}

			DataServer.CreateItem(name, _path, "image/gif", false, content.Length, content);
			return string.Empty;
		}

		public override string MoveFile(string path, string newPath)
		{
			try
			{
				bool destFileExists = this.DataServer.IsItemExists(newPath);
				if (destFileExists)
					return "A file with the same name exists in the destination folder";

				string newFileName = GetName(newPath);
				string destinationDirPath = newPath.Substring(0, newPath.Length - newFileName.Length);

				if (destinationDirPath.Length == 0)
				{
					destinationDirPath = path.Substring(0, path.LastIndexOf("/"));
				}
				// destination directory row
				DataRow newPathRow = DataServer.GetItemRow(destinationDirPath);

				DataServer.UpdateItemPath(path, newFileName, (int)newPathRow["ItemID"]);
			}
			catch (Exception e)
			{
				return e.Message;
			}
			return string.Empty;
		}

		public override string MoveDirectory(string path, string newPath)
		{
			if (newPath.EndsWith("/")) newPath = newPath.Remove(newPath.Length - 1, 1);

			bool destFileExists = this.DataServer.IsItemExists(newPath);
			if (destFileExists)
				return "A directory with the same name exists in the destination folder";

			return MoveFile(path, newPath);
		}

		public override string CopyFile(string path, string newPath)
		{
			try
			{
				bool destFileExists = this.DataServer.IsItemExists(newPath);
				if (destFileExists)
					return "A file with the same name exists in the destination folder";

				string newFileName = GetName(newPath);
				string newFilePath = newPath.Substring(0, newPath.Length - newFileName.Length);
				if (newFilePath.Length == 0)
				{
					newFilePath = path.Substring(0, path.LastIndexOf("/"));
				}
				DataRow oldPathRow = DataServer.GetItemRow(path);
				DataServer.CreateItem(newFileName, newFilePath, (string)oldPathRow["MimeType"], (bool)oldPathRow["IsDirectory"], (int)oldPathRow["Size"], DataServer.GetContent(path));
				if ((bool)oldPathRow["IsDirectory"])
				{
					//copy all child items of the folder as well
					FileItem[] files = GetChildFiles(path);
					foreach (FileItem childFile in files)
					{
						CopyFile(childFile.Tag, CombinePath(newPath, childFile.Name));
					}
					//copy all child folders as well
					DirectoryItem[] subFolders = GetChildDirectories(path);
					foreach (DirectoryItem subFolder in subFolders)
					{
						CopyFile(subFolder.Tag, CombinePath(newPath, subFolder.Name));
					}
				}
			}
			catch (Exception e)
			{
				return e.Message;
			}
			return string.Empty;
		}

		public override string CopyDirectory(string path, string newPath)
		{
			string[] pathParts = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
			if (pathParts.Length > 0)
			{
				string fullNewPath = CombinePath(newPath, pathParts[pathParts.Length - 1]);
				bool destFileExists = this.DataServer.IsItemExists(fullNewPath);
				if (destFileExists)
					return "A file with the same name exists in the destination folder";

				return CopyFile(path, fullNewPath);
			}
			else
				return "Old path is invalid";
		}

		public override string StoreFile(Telerik.Web.UI.UploadedFile file, string path, string name, params string[] arguments)
		{
			int fileLength = Convert.ToInt32(file.InputStream.Length);
			byte[] content = new byte[fileLength];
			file.InputStream.Read(content, 0, fileLength);
			string fullPath = CombinePath(path, name);
			if (!Object.Equals(DataServer.GetItemRow(fullPath), null))
			{
				DataServer.ReplaceItemContent(fullPath, content);
			}
			else
			{
				DataServer.CreateItem(name, path, file.ContentType, false, fileLength, content);
			}
			return string.Empty;
		}

		public override string DeleteFile(string path)
		{
			DataServer.DeleteItem(path);
			return string.Empty;
		}

		public override string DeleteDirectory(string path)
		{
			DataServer.DeleteItem(path);
			return string.Empty;
		}

		public override string CreateDirectory(string path, string name)
		{
			DataServer.CreateItem(name, path, string.Empty, true, 0, new byte[0]);
			return string.Empty;
		}

		public override bool CanCreateDirectory
		{
			get
			{
				return true;
			}
		}
	}
}